﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Bitcoin.Net
{
    public class NetAddress
    {
        byte[] bytes = new byte[16];

        public NetAddress(byte[] bytes)
        {
            if (bytes.Length != 4 && bytes.Length != 16) { throw new ArgumentException("Incorrect number of bytes for an address.", "bytes"); }

            if (bytes.Length == 4)
            {
                // IPv4-mapped IPv6 address
                this.bytes[10] = 0xFF;
                this.bytes[11] = 0xFF;
                for (int index = 0; index < 4; index++)
                {
                    this.bytes[index + 12] = bytes[index];
                }
            }
            else
            {
                for (int index = 0; index < bytes.Length; index++)
                {
                    this.bytes[index] = bytes[index];
                }                
            }
        }

        public byte[] Bytes
        {
            get { return bytes; }
        }

        public bool IsIPv4
        {
            get
            {
                for (int index = 0; index < 10; index++)
                {
                    if (bytes[index] != 0x0)
                    {
                        return false;
                    }
                }
                for (int index = 10; index < 12; index++)
                {
                    if (bytes[index] != 0xFF)
                    {
                        return false;
                    }
                }
                return true;
            }
        }

        public static NetAddress Parse(string s)
        {
            byte[] bytes;
            string[] v4 = s.Split(new char[] {'.'});
            if (v4.Length > 1)
            {
                bytes = new byte[4];
                for (int index = 0; index < 4; index++)
                {
                    bytes[index] = byte.Parse(v4[index]);
                }
            }
            else
            {
                string[] v6 = s.Split(new char[] {':'});
                bytes = new byte[16];
                var beforeCompressed = true;
                for (int index = 0; index < v6.Length; index++)
                {
                    if (v6[index] == string.Empty)
                    {
                        beforeCompressed = false;
                    }
                    else
                    {
                        var value = int.Parse(v6[index], NumberStyles.HexNumber);
                        if (beforeCompressed)
                        {
                            bytes[index*2] = (byte)(value >> 8);
                            bytes[index*2 + 1] = (byte)(value & 0xFF);
                        }
                        else
                        {
                            bytes[16 - (v6.Length - index)*2] = (byte) (value >> 8);
                            bytes[16 - (v6.Length - index)*2 + 1] = (byte) (value & 0xFF);
                        }
                    }
                }
            }
            var address = new NetAddress(bytes);
            return address;
        }

        public override string ToString()
        {
            if (IsIPv4)
            {
                var s = string.Format("{0}.{1}.{2}.{3}", bytes[12], bytes[13], bytes[14], bytes[15]);
                return s;
            }
            else
            {
                int[] words = new int[8];
                int longestRun = 0;
                int longestRunStart = -1;
                int thisRunStart = -1;
                for (int index = 0; index < 8; index++)
                {
                    var word = bytes[index*2] << 8 | bytes[index*2 + 1];
                    words[index] = word;
                    if (word == 0)
                    {
                        if (thisRunStart == -1)
                        {
                            thisRunStart = index;
                        }
                    }
                    else
                    {
                        if (thisRunStart >= 0)
                        {
                            var thisRun = index - thisRunStart;
                            if (thisRun >= 2 && thisRun > longestRun)
                            {
                                longestRun = thisRun;
                                longestRunStart = thisRunStart;
                            }
                            thisRunStart = -1;
                        }
                    }
                }

                var s = new StringBuilder();
                for (int index = 0; index < 8; index++)
                {
                    if (index >= longestRunStart && index < longestRunStart + longestRun)
                    {
                        if (index == longestRunStart)
                        {
                            s.Append(":");
                        }
                    }
                    else
                    {
                        if (index > 0)
                        {
                            s.Append(":");
                        }
                        s.AppendFormat("{0:x}", words[index]);
                    }
                }
                return s.ToString();
            }
        }
    }
}
